/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.projects;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import edu.cmu.ri.createlab.jj.JubJub.EmptyStringException;
import edu.cmu.ri.createlab.jj.blocks.BlockBlock;
import edu.cmu.ri.createlab.jj.blocks.CodeBlock;
import edu.cmu.ri.createlab.jj.blocks.CommentBlock;
import edu.cmu.ri.createlab.jj.blocks.ExpressionBlock;
import edu.cmu.ri.createlab.jj.blocks.FlowBlock;
import edu.cmu.ri.createlab.jj.blocks.ObjectBlock;
import edu.cmu.ri.createlab.jj.blocks.RefBlock;
import edu.cmu.ri.createlab.jj.blocks.TaggedCodePiece;
import edu.cmu.ri.createlab.jj.blocks.VarBlock;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.AccessTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.ExpressTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.VarTypes;
import edu.cmu.ri.createlab.jj.visitors.BVReturn;
import edu.cmu.ri.createlab.jj.visitors.BVXMLWriter;
import edu.cmu.ri.createlab.jj.visitors.BlockVisitorInterface;

public class JJProject
{
   ///The name of this library
   private String name;
   /**
    * A list of all the classes contained in this library.
    * Editor can define special classes, such as Flow or Operation
    * to determine what's in the default tabs.
    */
   private List<JJClass> classes;
   ///A list of libraries this project is dependent on
   private List<JJLibrary> libraries;
   ///A list of tabs this project defines
   private List<JJTab> tabs;
   
   ///Create a new library with the given name
   public JJProject(String name)
      throws EmptyStringException
   {
      if(name == null || name.length() < 1)
         throw new EmptyStringException("Empty library name");
      this.setName(name);
      this.classes = new LinkedList<JJClass>();
      this.libraries = new LinkedList<JJLibrary>();
      this.tabs = new LinkedList<JJTab>();
   }
   
   ///Set the name of this library
   public void setName(String name)
   {
      this.name = name;
   }

   ///get the name of this library
   public String getName()
   {
      return name;
   }

   ///add a library to this project
   public void addLibrary(JJLibrary library)
   {
      if(!hasLibrary(library))
         libraries.add(library);
   }
   
   ///remove a library from this project
   public void remLibrary(JJLibrary library)
   {
      libraries.remove(library);
   }
   
   ///Returns whether the given library is in this project
   public boolean hasLibrary(JJLibrary library)
   {
      return libraries.contains(library);
   }
   
   ///Returns whether a library with the given name is in this project
   public boolean hasLibrary(String name)
   {
      Iterator<JJLibrary> it = libraries.iterator();
      while(it.hasNext())
      {
         if(it.next().getName().equals(name))
            return true;
      }
      return false;
   }

   public List<JJLibrary> getLibraries()
   {
      return libraries;
   }
   
   ///add a tab to this project
   public void addTab(JJTab tab)
   {
      if(!hasTab(tab))
         tabs.add(tab);
   }

   ///add a tab to this project
   public void addTab(JJTab tab, int index)
   {
      if(!hasTab(tab))
         tabs.add(index, tab);
   }
   
   ///remove a tab from this project
   public void remTab(JJTab tab)
   {
      tabs.remove(tab);
   }
   
   ///Returns whether the given tab is in this project
   public boolean hasTab(JJTab tab)
   {
      return tabs.contains(tab);
   }
   
   ///Returns whether a tab with the given name is in this project
   public boolean hasTab(String name)
   {
      Iterator<JJTab> it = tabs.iterator();
      while(it.hasNext())
      {
         if(it.next().getName().equals(name))
            return true;
      }
      return false;
   }

   public List<JJTab> getTabs()
   {
      return tabs;
   }

   ///add a class to the library
   public void addClass(JJClass jjclass)
   {
      if(!hasClass(jjclass)) classes.add(jjclass);
   }
   
   ///remove a class from the library
   public void remClass(JJClass jjclass)
   {
      classes.remove(jjclass);
   }
   
   ///returns whether this library contains the given class
   public boolean hasClass(JJClass jjclass)
   {
      return classes.contains(jjclass);
   }
   
   ///returns whether this library contains a class of the given name
   public boolean hasClass(String name)
   {
      Iterator<JJClass> it = classes.iterator();
      while(it.hasNext())
      {
         if(it.next().getName().equals(name))
            return true;
      }
      return false;
   }
   
   ///returns the class with the given name or null if it doesn't exist
   public JJClass getClass(String name)
   {
      Iterator<JJClass> it = classes.iterator();
      while(it.hasNext())
      {
         JJClass tempClass = it.next();
         if(tempClass.getName().equals(name))
            return tempClass;
      }
      return null;
   }

   ///returns a list of classes in this library
   public List<JJClass> getClasses()
   {
      return classes;
   }
   
   ///allows a BlockVisitor to visit this object
   public BVReturn accept(BlockVisitorInterface bv)
   {
      return bv.visit(this);
   }
   
   public static JJProject getTestProject()
   {
      JJProject helloWorldProj = null;
      JJClass helloWorld; 
      CodeBlock hwConst, sayHello;
      BlockBlock main;
      ObjectBlock name, hello, name2, hwArgs;
      VarBlock i, litName, litName2, exp1, exp2, one;
      RefBlock sayHelloRef, makeHW, helloRef, whileRef, whileRef2;
      ExpressionBlock setName, combineName, cLT5, ipp;
      CommentBlock cb, cb2;
      FlowBlock while1;
            
      AccessTypes[] pubAccess = {AccessTypes.PUBLIC};
      AccessTypes[] pubStatAcc = {AccessTypes.PUBLIC, AccessTypes.STATIC};
      
      LinkedList<TaggedCodePiece> sayHelloCode = new LinkedList<TaggedCodePiece>();
      sayHelloCode.add(new TaggedCodePiece("System.out.println(\"Hello \" + "));
      sayHelloCode.add(new TaggedCodePiece("name", 0));
      sayHelloCode.add(new TaggedCodePiece(" + \"!\");"));
      
      LinkedList<TaggedCodePiece> whileStart = new LinkedList<TaggedCodePiece>();
      whileStart.add(new TaggedCodePiece("while("));
      whileStart.add(new TaggedCodePiece("exp", 0));
      whileStart.add(new TaggedCodePiece(")"));
      //LinkedList<TaggedCodePiece> whileEnd = new LinkedList<TaggedCodePiece>();
      
      try
      {

         helloWorldProj = new JJProject("Hello World");
         JJSubTab subT = new JJSubTab("test stab");
         JJTab tab = new JJTab("test tab");
         tab.addSubTab(subT);
         helloWorldProj.addTab(tab);
         helloWorld = new JJClass("Hello World");
         helloWorldProj.addClass(helloWorld);
         
         hwConst = new CodeBlock("Hello World", helloWorld, 1);
         hwConst.setAccess(pubAccess);
         hwConst.setConstructor(true);
         helloWorld.addConstructor(hwConst);
         
         helloWorld.setComment("This is a class comment");
         
         ///Name of the VarBlock must be the same as the name in the tagged code
         name = new ObjectBlock("name", null, 2, 3, null);
         name.setObjectType("String");
         
         sayHello = new CodeBlock("say Hello", helloWorld, 4);
         sayHello.setTaggedCode(sayHelloCode);
         sayHello.addParam(name.getVariable());
         sayHello.setComment("This is a method comment");
         
         VarBlock boolRet = new VarBlock("Erik", null, 19, VarTypes.BOOLEAN, true);
         boolRet.setValue("true", 0);
         
         //sayHello.addReturn(boolRet);
         
         helloWorld.addMethodBlock(sayHello);
         
         hello = new ObjectBlock("hello",null, 5, 6, helloWorld);
         helloWorld.addInstance(hello);
         hello.getVariable().setComment("Comments to the right");
         
         i = new VarBlock("i", null, 7, VarTypes.INT);
         i.setComment("Comments to the left of me");
         
         name2 = new ObjectBlock("name", null, 8, 9, null);
         name2.setObjectType("String");
         
         hwArgs = new ObjectBlock("args", null, 10, 11, null);
         hwArgs.setObjectType("String[]");
         
         sayHelloRef = new RefBlock(12, sayHello);
         sayHelloRef.addParam(new RefBlock(13, name2.getVariable()));
         //sayHelloRef.addReturn(new RefBlock(100, null));
         
         makeHW = new RefBlock(14, hello);
         makeHW.addParam(new RefBlock(15,hwConst));
         
         helloRef = new RefBlock(16, hello);
         helloRef.addParam(sayHelloRef);
         helloRef.setComment("Stuck in the middle with you");
         
         RefBlock hr2 = new RefBlock(97, hello);
         RefBlock shr2 = new RefBlock(98, sayHello);
         shr2.addParam(new RefBlock(99, null));
         shr2.addReturn(new RefBlock(99, null));
         hr2.addParam(shr2);
         subT.addBlock(hr2);
         
         
         litName = new VarBlock("Erik", null, 19, VarTypes.OBJECT, true);
         litName.setObjectType("String");
         litName.setValue("\"Erik\"", 0);
         
         RefBlock lnr = new RefBlock(1000, name.getVariable());
         subT.addBlock(lnr);
         
         JJSubTab subT2 = new JJSubTab("sTab 2");
         
         litName2 = new VarBlock("Pasternak", null, 23, VarTypes.OBJECT, true);
         litName2.setObjectType("String");
         litName2.setValue("\" Pasternak\"", 0);
                
         combineName = new ExpressionBlock(null, 24, ExpressTypes.ARITH, "+");
         combineName.setFirstIn(new RefBlock(25,litName));
         combineName.setSecondIn(new RefBlock(26,litName2));
         
         subT2.addBlock(new RefBlock(1001, combineName));
         tab.addSubTab(subT2);
         
         RefBlock combineNameRef = new RefBlock(20, combineName);
         combineNameRef.addParam(combineName.getFirstIn());
         combineNameRef.addParam(combineName.getSecondIn());
         
         setName = new ExpressionBlock(null, 17, ExpressTypes.ASSIGN, "=");
         setName.setFirstIn(new RefBlock(18,name2.getVariable()));
         setName.setSecondIn(combineNameRef);
         
         RefBlock setNameRef = new RefBlock(22, setName);
         setNameRef.addParam(setName.getFirstIn());
         setNameRef.addParam(setName.getSecondIn());
         
         exp1 = new VarBlock("exp", null, 28, VarTypes.BOOLEAN);
         
         while1 = new FlowBlock("while", null, 27);
         while1.setCode(whileStart, 0);
         while1.addParam(exp1, 0);
         
         exp2 = new VarBlock("five", null, 32, VarTypes.INT, true);
         exp2.setValue("5", 0);
         
         cLT5 = new ExpressionBlock(null, 29, ExpressTypes.LOGIC, "<");
         RefBlock cLT5Ref = new RefBlock(105, cLT5);
         cLT5Ref.addParam(new RefBlock(31, i));
         cLT5Ref.addParam(new RefBlock(33, exp2));
         
         whileRef = new RefBlock(30, while1);
         whileRef.addParam(cLT5Ref);
         whileRef.setIndex(0);
         whileRef.setComment("Here I am");
         
         whileRef2 = new RefBlock(35, while1);
         whileRef2.setIndex(3);
         
         one = new VarBlock("one", null, 38, VarTypes.INT, true);
         one.setValue("1", 0);
         
         ExpressionBlock ip1 = new ExpressionBlock(null, 101, ExpressTypes.ARITH, "+");
         RefBlock ip1Ref = new RefBlock(102, ip1);
         ip1Ref.addParam(new RefBlock(103, i));
         ip1Ref.addParam(new RefBlock(104, one));
         
         ipp = new ExpressionBlock(null, 36, ExpressTypes.ASSIGN, "=");
         ipp.setFirstIn(new RefBlock(37,i));
         ipp.setSecondIn(ip1Ref);
         
         RefBlock ippRef = new RefBlock(40, ipp);
         ippRef.addParam(ipp.getFirstIn());
         ippRef.addParam(ipp.getSecondIn());
         
         cb = new CommentBlock("c1", null, 41);
         cb.setComment("CommentBlock can has comment");
         cb2 = new CommentBlock("c2", null, 41);
         cb2.setComment("CommentBlock is not amused");
         
         main = new BlockBlock("main", helloWorld, 21);
         main.setAccess(pubStatAcc);
         main.addParam(hwArgs.getVariable());
         main.addVar(i);
         main.addVar(hello.getVariable());
         main.addVar(name2.getVariable());
         main.addCode(makeHW);
         main.addCode(setNameRef);
         main.addCode(whileRef);
         main.addCode(helloRef);
         main.addCode(ippRef);
         main.addCode(new RefBlock(43, cb));
         main.addCode(whileRef2);
         main.addCode(new RefBlock(42,cb2));
         
         helloWorld.addMethodBlock(main);
                  
         
      } catch(Exception e)
      {
         System.err.println(e.getMessage());
      }
      return helloWorldProj;
   }
}
